<?php
namespace app\common\model\data;

use app\common\helper\Obj2Array;
use app\common\model\project_model\SystemMusicModel;
use think\cache\driver\Redis;
use think\Exception;

class SystemMusicSpiderModel {
    public static $err_msg = '';

    const QQ_PREFIX_FILENAME = 'C400'; // qq音乐获取vkey时拼接filename的前缀
    const QQ_SUFFIX_FILENAME = '.m4a'; // qq音乐获取vkey时拼接filename的后缀
    const QQ_PLAY_HOST = 'http://dl.stream.qqmusic.qq.com'; // qq音乐获取vkey时拼接filename的前缀
    // QQ音乐关键字搜索音乐list api
    const QQ_API_MUSIC_SEARCH = "https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=txt.yqq.song&cr=1&n=%d&w=%s&g_tk=5381&format=json&inCharset=utf8&outCharset=utf-8";
    // QQ音乐获取vkey api
    const QQ_API_V_KEY = "https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg?g_tk=678733985&jsonpCallback=MusicJsonCallback8015407264426806&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0&cid=205361747&callback=MusicJsonCallback8015407264426806&songmid=%s&filename=%s&guid=%d";
    // QQ音乐获取播放地址
    const QQ_API_PLAY_URL = "%s/%s?guid=%d&vkey=%s&fromtag=66";

    /**
     * 根据mid返回QQ音乐的播放地址
     *
     * @param $mid
     * @return bool|string
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function getQQPlayUrl($mid) {

        $musicM = new SystemMusicModel();
        $music = $musicM->where(['music_id' => $mid])->field('music_id, g_uid, type, mp3_url')->find();
        if (empty($music)) {
            static::$err_msg = '没有该音乐';
            return false;
        }
        // 系统音乐，直接从系统中取出
        if (($music['type'] == $musicM::TYPE_SYSTEM) && $music['mp3_url']) {
            return $music['mp3_url'];
        }

        // 数据不全
        if ((($music['type'] == $musicM::TYPE_SYSTEM) && !$music['mp3_url']) || (($music['type'] == $musicM::TYPE_OUT) && (!$music['music_id'] || !$music['g_uid']))) {
            self::$err_msg = '该音乐数据不全';
            return false;
        }

        // 外链音乐
        if (!$url = self::getUrlFromRedis($mid, $music['g_uid'])) {
            if (!$v_key = self::getVKey($mid, $music['g_uid'])) {
                return false;
            } else {
                $url = sprintf(self::QQ_API_PLAY_URL, self::QQ_PLAY_HOST, self::QQ_PREFIX_FILENAME . $mid . self::QQ_SUFFIX_FILENAME, $music['g_uid'], $v_key);
            }
        }

        return $url;
    }

    /**
     * 从redis获取音乐播放url
     *
     * @param $mid
     * @param $g_uid
     * @return bool|string
     */
    public static function getUrlFromRedis($mid, $g_uid) {
        $redis = new Redis();
        $redis_mid_key = 'Redis_mid_key_' . $mid;
        $v_key = $redis->get($redis_mid_key);
        if (empty($v_key)) {
            return false;
        }
        $url = sprintf(self::QQ_API_PLAY_URL, self::QQ_PLAY_HOST, self::QQ_PREFIX_FILENAME . $mid . self::QQ_SUFFIX_FILENAME, $g_uid, $v_key);
        return $url;
    }

    /**
     * 保存vkey到redis
     *
     * @param $mid
     * @param $v_key
     * @return bool
     */
    public static function saveKeyByRedis($mid, $v_key) {
        $redis = new Redis();
        $redis_mid_key = 'Redis_mid_key_' . $mid;
        return $redis->set($redis_mid_key, $v_key, 75600);
    }

    /**
     * qq 音乐获取vkey
     *
     * @param $mid
     * @param $guid
     * @return bool
     */
    public static function getVKey($mid, $guid) {
        $filename = self::QQ_PREFIX_FILENAME . $mid . self::QQ_SUFFIX_FILENAME;
        $url = sprintf(SystemMusicSpiderModel::QQ_API_V_KEY, $mid, $filename, $guid);
        $res = self::getByCurl($url);
        if (!$res) {
            return false;
        }
        $data = explode('(', $res);
        $str = substr_replace($data[1], '', -1);
        $arr = json_decode($str,true);
        $v_key = $arr['data']['items'][0]['vkey'];
        if (empty($v_key)) {
            self::$err_msg = '该音乐已经下架！';
            return false;
        }
        self::saveKeyByRedis($mid, $v_key);
        return $v_key;
    }

    /**
     * 简单的get请求
     *
     * @param $url
     * @param bool $forge 是否启用伪造，默认不启用
     * @return bool|mixed
     */
    public static function getByCurl($url, $forge = false) {
        $ch =curl_init();
        curl_setopt($ch,CURLOPT_URL,$url);
        //curl_setopt($ch,CURLOPT_,true);
        curl_setopt($ch,CURLOPT_POST,false);
        //curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
        //curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
        curl_setopt($ch,CURLOPT_HEADER,true);
        //curl_setopt($ch,CURLOPT_HTTPHEADER,$header);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        if ($forge) { // 是否伪造
            $user_agent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36";//模拟windows用户正常访问
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:'.self::randIp(), 'CLIENT-IP:'.self::randIp())); // 伪造ip
            curl_setopt($ch, CURLOPT_REFERER, 'http://www.baidu.com/'); // 伪造来源
            curl_setopt($ch, CURLOPT_USERAGENT, $user_agent); // 伪造浏览器
        }

        try {
            $content = curl_exec($ch);
            $err = curl_errno($ch);
            curl_close($ch);
            if ($err) {
                throw new Exception($url . '请求失败：' . $err);
            }
        } catch(\Exception $e) {
            static::$err_msg = $e->getMessage();
            return false;
        }
       return $content;
    }

    /**
     * 伪造ip
     *
     * @return string
     */
    public static function randIp(){
        $ip2id= round(rand(600000, 2550000) / 10000); //第一种方法，直接生成
        $ip3id= round(rand(600000, 2550000) / 10000);
        $ip4id= round(rand(600000, 2550000) / 10000);
        //下面是第二种方法，在以下数据中随机抽取
        $arr_1 = array("218","218","66","66","218","218","60","60","202","204","66","66","66","59","61","60","222","221","66","59","60","60","66","218","218","62","63","64","66","66","122","211");
        $randarr= mt_rand(0,count($arr_1)-1);
        $ip1id = $arr_1[$randarr];
        return $ip1id.".".$ip2id.".".$ip3id.".".$ip4id;
    }

}